/*
*  Copyright (c) 2001 Sun Microsystems, Inc.  All rights
*  reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*  1. Redistributions of source code must retain the above copyright
*  notice, this list of conditions and the following disclaimer.
*
*  2. Redistributions in binary form must reproduce the above copyright
*  notice, this list of conditions and the following disclaimer in
*  the documentation and/or other materials provided with the
*  distribution.
*
*  3. The end-user documentation included with the redistribution,
*  if any, must include the following acknowledgment:
*  "This product includes software developed by the
*  Sun Microsystems, Inc. for Project JXTA."
*  Alternately, this acknowledgment may appear in the software itself,
*  if and wherever such third-party acknowledgments normally appear.
*
*  4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA"
*  must not be used to endorse or promote products derived from this
*  software without prior written permission. For written
*  permission, please contact Project JXTA at http://www.jxta.org.
*
*  5. Products derived from this software may not be called "JXTA",
*  nor may "JXTA" appear in their name, without prior written
*  permission of Sun.
*
*  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
*  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
*  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
*  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
*  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
*  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
*  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*  SUCH DAMAGE.
*  ====================================================================
*
*  This software consists of voluntary contributions made by many
*  individuals on behalf of Project JXTA.  For more
*  information on Project JXTA, please see
*  <http://www.jxta.org/>.
*
*  This license is based on the BSD license adopted by the Apache Foundation.
*
*  $Id: NetBeamsChart.java,v 1.2 2006/07/13 05:26:37 nano Exp $
*/

package net.jxta.myjxta.misc.beam;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.util.Date;
import java.util.Properties;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;


/**
 * Tracks Memory allocated & used, displayed in graph form.
 */
public class NetBeamsChart extends JPanel {

    public Surface surf;
    String desc = null;
    String gid = null;
    String sid = null;
    String units = null;
    String type = null;
    String url = null;
    String vxpath = null;
    float gpsx = 0.0f;
    float gpsy = 0.0f;
    float scalefactor = 1.0f;

    public NetBeamsChart(Properties p) {
        this.desc = p.getProperty("desc");
        this.units = p.getProperty("units");
        this.gid = p.getProperty("gridid");
        this.sid = p.getProperty("sid");
        this.type = p.getProperty("type");
        this.url = p.getProperty("url");
        this.vxpath = p.getProperty("vxpath");
        this.gpsx = Float.parseFloat(p.getProperty("gpsx"));
        this.gpsy = Float.parseFloat(p.getProperty("gpsy"));
        this.scalefactor = Float.parseFloat(p.getProperty("scalefactor"));
        setLayout(new BorderLayout());
        setBorder(new TitledBorder(new EtchedBorder(), "Sensor: " + sid + " - " + desc));
        add(surf = new Surface());
    }

    public String getSensorID() {
        return sid;
    }

    public String getValueXPath() {
        return vxpath;
    }

    public String getSensorURL() {
        return url;
    }

    public String getDescription() {
        return desc;
    }

    public String getSensorType() {
        return type;
    }

    public String getSensorUnits() {
        return units;
    }

    public float getSensorGPSXCoordinate() {
        return gpsx;
    }

    public float getSensorGPSYCoordinate() {
        return gpsy;
    }

    public class Surface extends JPanel implements Runnable {

        public Thread thread;
        public long sleepAmount = 1000;
        public long lastDraw = 0L;
        public boolean fullDrawNeeded = false;
        private int w, h;
        private BufferedImage bimg;
        private Graphics2D big;
        private Font font = new Font("Times New Roman", Font.PLAIN, 11);
        private FontMetrics fm = null;
        private Runtime r = Runtime.getRuntime();
        private int columnInc;
        private float pts[];
        private int ptNum;
        private int ascent, descent;
        private Rectangle graphOutlineRect = new Rectangle();
        private Line2D graphLine = new Line2D.Float();
        private Color graphColor = new Color(46, 139, 87);
        private float currVal = 0.0f;
        private long lastwritetime = 0L;
        private long writeInterval = 5000L;

        // set some start values here.  If you want the graph
        // graph will adjust over time, but only when boundaries
        // of min and max have been exceeded
        private float minval = 0.0f;
        private float maxval = 10.0f;

        // pick number of rows as 5 or 10 to help with decimals
        public int NROWS = 6;
        public int NCOLS = 12;

        public Surface() {
            setBackground(Color.black);
            setPreferredSize(new Dimension(135, 80));
        }

        public Dimension getMinimumSize() {
            return getPreferredSize();
        }

        public Dimension getMaximumSize() {
            return getPreferredSize();
        }

        public Dimension getPreferredSize() {
            return new Dimension(135, 80);
        }

        public void setCurrentIntValue(float val) {
            currVal = val;
        }

        public float getCurrentValue() {
            return currVal;
        }

        public void paint(Graphics g) {

            if (big == null) {
                return;
            }
            long currDraw = System.currentTimeMillis();
            if ((currDraw - lastDraw) > sleepAmount) {
                fullDrawNeeded = true;
                //System.out.println("fullDrawNeeded = true");
                lastDraw = currDraw;
            }

            // Calculate dimensions
            float ssH = ascent + descent;
            float remainingHeight = (h - (ssH * 2) - 0.5f);
            int graphX = 35;
            int graphY = (int) ssH;
            int graphW = w - graphX - 5;
            int graphH = (int) remainingHeight;

            // initialize the data buffer if necessary. Make the number of
            // points equal to the pixel width of the graph
            if (pts == null) {
                pts = new float[graphW];
                ptNum = 0;
                // else if we've resized the window, we need to resize buffer
            } else if (pts.length != graphW) {
                float[] tmp = new float[graphW];
                if (ptNum < graphW) { // window is wider than data
                    System.arraycopy(pts, 0, tmp, 0, ptNum);
                } else { // more data than fits window       
                    System.arraycopy(pts, ptNum - graphW, tmp, 0, graphW - 1);
                    ptNum = graphW - 1;
                }
                pts = tmp;
            }

            // go fetch next value of data
            pts[ptNum] = getCurrentValue();
            //System.out.println("currentValue = " +pts[ptNum]);
            if (fullDrawNeeded) ptNum++;

            // find the min/max within valid data
            if (ptNum > 0) {
                minval = pts[ptNum - 1];
                maxval = pts[ptNum - 1];
            }
            for (int i = 0; i < ptNum; i++) {
                minval = Math.min(minval, pts[i]);
                maxval = Math.max(maxval, pts[i]);
            }
            //System.out.println("minval = " + minval +", maxval = "+maxval);

            // determine the min/max chart range make it sticky and stable
            // to prevent replotting the chart lines all the time
            // also, give some head and tail room
            float diff = 1.5f * (maxval - minval) / NROWS;
            int incr = 1;
            while (diff > 10.0f) {
                diff /= 10.0f;
                incr *= 10;
            }

            if (diff > 5.0f) {
                incr *= 10;
            } else if (diff > 2.0f) {
                incr *= 5;
            } else if (diff > 1.0f) {
                incr *= 2;
            }

            int totalspan = incr * NROWS;
            int ymin = (int) ((maxval + minval - totalspan) / 2);
            ymin = ((int) (ymin / incr)) * incr;
            int ymax = ymin + totalspan;

            big.setBackground(getBackground());
            big.clearRect(0, 0, w, h);

            // .. Draw axis strings ..
            big.setColor(Color.green);
            float currVal0 = ((int) (currVal * 10)) / 10.0f;
            big.drawString(units + "        [current val = " + currVal0 + "]", 35, ascent + 1);
            big.drawString("Time:  " + (new Date()), 40, h - descent);

            // .. Draw History Graph ..
            big.setColor(graphColor);
            graphOutlineRect.setRect(graphX, graphY, graphW, graphH);
            big.draw(graphOutlineRect);

            int graphRow = graphH / NROWS;

            // .. Draw rows and unit scale  ..
            int yval = ymax;
            for (int j = graphY; j <= graphH + graphY; j += graphRow) {
                graphLine.setLine(graphX, j, graphX + graphW, j);
                big.setColor(graphColor);
                big.draw(graphLine);
                big.setColor(Color.green);
                String yvalstr = Integer.toString(yval);
                if (fm == null) fm = g.getFontMetrics();
                int swidth = fm.stringWidth(yvalstr);
                big.drawString(yvalstr, 33 - swidth, j + 4);
                yval -= incr;
            }

            // .. Draw animated column movement ..
            int graphColumn = graphW / NCOLS;
            big.setColor(graphColor);

            if (columnInc == 0) {
                columnInc = graphColumn;
            }

            for (int j = graphX + columnInc; j < graphW + graphX; j += graphColumn) {
                graphLine.setLine(j, graphY, j, graphY + graphH);
                big.draw(graphLine);
            }

            if (fullDrawNeeded) --columnInc;

            // paint the chart
            big.setColor(Color.yellow);
            for (int j = graphX + graphW - ptNum, k = 0; k < ptNum; k++, j++) {
                if (k != 0) {
                    if (pts[k] != pts[k - 1]) {
                        big.drawLine(j - 1, (int) (graphY + graphH - graphH * (pts[k - 1] - ymin) / totalspan), j, (int) (graphY + graphH - graphH * (pts[k] - ymin) / totalspan));

                    } else {
                        big.fillRect(j, (int) (graphY + graphH - graphH * (pts[k] - ymin) / totalspan), 1, 1);
                    }
                }
            }
            if (ptNum + 2 >= pts.length) {
                // throw out oldest point
                for (int j = 1; j < ptNum; j++) {
                    pts[j - 1] = pts[j];
                }
                --ptNum;
            }
            g.drawImage(bimg, 0, 0, this);
            fullDrawNeeded = false;
        }


        public void start() {
            thread = new Thread(this);
            thread.setPriority(Thread.MIN_PRIORITY);
            thread.setName("NetBEAMS-" + sid);
            thread.start();
        }


        public synchronized void stop() {
            thread = null;
            notify();
        }

        public void run() {

            Thread me = Thread.currentThread();

            while (thread == me && !isShowing() || getSize().width == 0) {
                try {
                    Thread.sleep(600);
                } catch (InterruptedException e) {
                    return;
                }
            }

            while (thread == me && isShowing()) {
                Dimension d = getSize();
                if (d.width != w || d.height != h) {
                    w = d.width;
                    h = d.height;
                    bimg = (BufferedImage) createImage(w, h);
                    big = bimg.createGraphics();
                    big.setFont(font);
                    fm = big.getFontMetrics(font);
                    ascent = (int) fm.getAscent();
                    descent = (int) fm.getDescent();
                }

                repaint();

                try {
                    Thread.sleep(sleepAmount);

                    // retrieve the URL data
                    String xmldoc = DataManager.retrieveURLFile(url);
                    String path = System.getenv("HOME");
                    path += "/public_html/" + gid + "/" + sid + "/" + System.currentTimeMillis() + ".xml";
                    DataManager.prepDirectory(path);
                    long currtimemillis = System.currentTimeMillis();
                    long elapsed = currtimemillis - lastwritetime;
                    if (elapsed > writeInterval) {
                        DataManager.saveToFile(xmldoc, path);
                        lastwritetime = currtimemillis;

                        String value = NRSSDataExtractor.getValueFromURL(url, vxpath);
                        //System.out.println("curr value: " + value);

                        try {
                            currVal = scalefactor * Float.parseFloat(value);
                        } catch (Exception exc) {
                            exc.printStackTrace();
                        }
                    }

                } catch (InterruptedException e) {
                    break;
                }
            }
            thread = null;
        }
    }

    public static void main(String s[]) throws Exception {
        Properties p = new Properties();
        p.setProperty("url", "http://198.144.203.235/~jcliu/netbeams.xml");
        p.setProperty("sid", "00001");
        p.setProperty("type", "temperature");
        p.setProperty("units", "Celsius");
        p.setProperty("desc", "Generic Temperature Sensor");
        p.setProperty("scalefactor", "1.0");
        p.setProperty("gpsx", "-122.0000");
        p.setProperty("gpsy", "37.0000");
        p.setProperty("vxpath", "/rss/channel/item/description/nrss:data/nrss:content/nrss:argArray[@n='current']/nrss:arg[@n='value']");
        final NetBeamsChart demo = new NetBeamsChart(p);
        WindowListener l = new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }

            public void windowDeiconified(WindowEvent e) {
                demo.surf.start();
            }

            public void windowIconified(WindowEvent e) {
                demo.surf.stop();
            }
        };
        JFrame f = new JFrame("NetBeams Chart");
        f.addWindowListener(l);
        f.getContentPane().add("Center", demo);
        f.pack();
        f.setSize(new Dimension(500, 200));
        f.setVisible(true);
        demo.surf.start();
    }
}
